﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FunctionGraph
{
    struct Rational
    {
        public double? Numerator;
        public double? Denominator;

        public Rational(double? numerator, double denom)
        {
            this.Numerator = numerator;
            this.Denominator = denom;
            if (numerator.HasValue)
            {
                if (numerator.Value > 0 && denom > 0 && (numerator)==(int)(numerator) && denom==(int)denom)
                {
                    int g = DenominatorExtractor.GCD((int)numerator.Value, (int)denom);
                    this.Numerator /= g;
                    this.Denominator /= g;
                }
            }
        }
    }
    class DenominatorExtractor : ExpressionVisitor<Rational>
    {
        public static int GCD(int x, int y)
        {
            if (x <= 0 || y <= 0)
                throw new ArgumentException("argument should be larger than 1");
            while (y > 0)
            {
                int t = x;
                x = y;
                y = t % y; 
            }
            return x;
        }
        static int LCM(int x, int y)
        {
            if (x <= 0 || y <= 0)
                throw new ArgumentException("argument should be larger than 1");
            return x * y / GCD(x, y);
        }
        public override Rational VisitConstant(ConstantNode constantNode)
        {
            return new Rational(constantNode.Value, 1);
        }

        public override Rational VisitVariable(VariableNode variableNode)
        {
            return new Rational(null, 1);
        }

        public override Rational VisitBinary(BinaryNode binaryNode)
        {
            var d1 = binaryNode.Left.Accept(this);
            var d2 = binaryNode.Right.Accept(this);
            switch (binaryNode.Operator)
            {
                case MathOperator.Add:
                case MathOperator.Sub:
                    {
                        if (d1.Denominator.HasValue && d2.Denominator.HasValue)
                        {
                            double denom = d1.Denominator.Value * d2.Denominator.Value;
                            double? numerator = null;
                            if (d1.Numerator.HasValue && d2.Numerator.HasValue)
                            {
                                double numerator1 = (denom / d1.Denominator.Value) * d1.Numerator.Value;
                                double numerator2 = (denom / d2.Denominator.Value) * d2.Numerator.Value;
                                if (binaryNode.Operator == MathOperator.Add)
                                    numerator = numerator1 + numerator2;
                                else
                                    numerator = numerator1 - numerator2;
                            }
                            return new Rational(numerator, denom);
                        }

                    }
                    break;
                case MathOperator.Mul:
                    {
                        if (d1.Denominator.HasValue && d2.Denominator.HasValue)
                        {
                            double denom = d1.Denominator.Value * d2.Denominator.Value;
                            double? numerator = null;
                            if (d1.Numerator.HasValue && d2.Numerator.HasValue)
                            {
                                double numerator1 = d1.Numerator.Value;
                                double numerator2 = d2.Numerator.Value;
                                numerator = numerator1 * numerator2;
                            }
                            return new Rational(numerator, denom);
                        }
                    }
                    break;
                case MathOperator.Div:
                    {
                        if (d1.Denominator.HasValue && d2.Numerator.HasValue)
                        {
                            double denom = d1.Denominator.Value * d2.Numerator.Value;
                            double? numerator = null;
                            if (d1.Numerator.HasValue && d2.Denominator.HasValue)
                            {
                                double numerator1 = d1.Numerator.Value;
                                double numerator2 = d2.Denominator.Value;
                                numerator = numerator1 * numerator2;
                            }
                            return new Rational(numerator, denom);
                        }
                    }
                    break;
            }
            return new Rational();
        }

        public override Rational VisitUnary(UnaryNode unaryNode)
        {
            if (unaryNode.Operator == MathOperator.Neg)
                return unaryNode.Child.Accept(this);
            return new Rational();
        }
    }
    class ExponentialVisitor : ExpressionVisitor<object>
    {
        public override object VisitConstant(ConstantNode constantNode)
        {
            return null;
        }

        public override object VisitVariable(VariableNode variableNode)
        {
            return null;
        }

        public override object VisitBinary(BinaryNode binaryNode)
        {
            binaryNode.Left.Accept(this);
            binaryNode.Right.Accept(this);
            if (binaryNode.Operator == MathOperator.Pow)
            {
                var v = new DenominatorExtractor();
                var r = binaryNode.Right.Accept(v);
                if (r.Denominator.HasValue && r.Denominator.Value == (int)r.Denominator.Value && r.Numerator.HasValue)
                {
                    var i = (int)r.Denominator.Value;
                    if ((i & 1) == 1 && i > 1)
                    {
                        var newLeft = new BinaryNode();
                        newLeft.Operator = MathOperator.Pow;
                        newLeft.Left = binaryNode.Left;
                        var cn = new ConstantNode();
                        cn.Value = r.Numerator.Value;
                        newLeft.Right = cn;
                        binaryNode.Left = newLeft;
                        binaryNode.Operator = MathOperator.Pow1;
                        cn = new ConstantNode();
                        cn.Value = 1.0/r.Denominator.Value;
                        binaryNode.Right = cn;
                    }
                }
            }
            return null;
        }

        public override object VisitUnary(UnaryNode unaryNode)
        {
            return unaryNode.Child.Accept(this);
        }
    }
}
